package com.qqt.csr.im.socketio.listener;

import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.AuthorizationResult;
import com.corundumstudio.socketio.HandshakeData;
import com.google.common.collect.Maps;
import com.qqt.csr.common.enums.DeviceTypeEnum;
import com.qqt.csr.common.utils.IPUtil;
import com.qqt.csr.common.utils.JwtSignerUtil;
import com.qqt.csr.common.utils.ServletUtils;
import com.qqt.csr.im.entity.Account;
import com.qqt.csr.im.service.AccountService;
import io.netty.handler.codec.http.HttpHeaders;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Map;

import static cn.hutool.core.net.NetUtil.isUnknown;

/**
 * IM授权校验
 */
@Slf4j
@Component
@DependsOn("jwtSignerUtil")
public class IMAuthorizationListener implements AuthorizationListener {
    @Value("${socketio.app.token.expire:54000000}")
    private long tokenExpire;

    @Override
    public AuthorizationResult getAuthorizationResult(HandshakeData data) {
        String token = data.getSingleUrlParam("token");
        //todo 只支持单设备
        // String deviceFlag = data.getSingleUrlParam("deviceFlag");
        // String deviceType = data.getSingleUrlParam("deviceType");
        String deviceFlag = "0";
        String deviceType = DeviceTypeEnum.WEB.getCode().toString();
        log.info("校验授权信息，socketio握手数据：urlParam:{},ip:{}", data.getUrlParams(), getRequestIp(data));
        if (StringUtils.isBlank(token)) {
            log.warn("非法链接，token为空");
            return AuthorizationResult.FAILED_AUTHORIZATION;
        }

        if (!JWTUtil.verify(token, JwtSignerUtil.getSigner())) {
            log.warn("非法链接，token签名无效，token：{}", token);
            return AuthorizationResult.FAILED_AUTHORIZATION;
        }

        JSONObject payloads = JWTUtil.parseToken(token).getPayloads();
        Long timestamp = payloads.getLong("timestamp");
        if ((timestamp == null || timestamp <= 0) || System.currentTimeMillis() - timestamp > tokenExpire) {
            log.warn("非法链接，token已过期，token：{}", token);
            return AuthorizationResult.FAILED_AUTHORIZATION;
        }

        Long accountId = payloads.getLong("accountId");
        String accountUserId = payloads.getStr("accountUserId");
        String tenantId = payloads.getStr("tenantId");
        if ((accountId == null || accountId <= 0L) || StringUtils.isAnyBlank(tenantId, accountUserId, deviceFlag, deviceType)) {
            log.warn("非法连接，参数异常，accountId:{}，token：{}", accountId, token);
            return AuthorizationResult.FAILED_AUTHORIZATION;
        }

        Map<String, Object> storeParams = Maps.newHashMap();
        storeParams.put("tenantId", tenantId);
        storeParams.put("accountId", accountId);
        storeParams.put("accountUserId", accountUserId);
        storeParams.put("deviceFlag", deviceFlag);
        storeParams.put("deviceType", deviceType);

        return new AuthorizationResult(true, storeParams);
    }

    /**
     * 获取客户端IP
     *
     * @return IP地址
     */
    public String getRequestIp(HandshakeData data) {
        HttpHeaders httpHeaders = data.getHttpHeaders();
        String ip = httpHeaders.get("x-forwarded-for");
        if (isUnknown(ip)) {
            ip = httpHeaders.get("X-Forwarded-For");
        }
        if (isUnknown(ip)) {
            ip = httpHeaders.get("X-Real-IP");
        }
        if (isUnknown(ip)) {
            ip = httpHeaders.get("Proxy-Client-IP");
        }
        if (isUnknown(ip)) {
            ip = httpHeaders.get("WL-Proxy-Client-IP");
        }
        if (isUnknown(ip)) {
            ip = data.getAddress().getAddress().getHostAddress();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : IPUtil.getMultistageReverseProxyIp(ip);
    }

}
